package com.mavenq.fly.utils.excelv4.example;

import com.mavenq.fly.utils.excelv4.ExcelUtil;
import com.mavenq.fly.utils.excelv4.config.ExcelExportConfig;
import com.mavenq.fly.utils.excelv4.config.ExcelImportConfig;
import com.mavenq.fly.utils.excelv4.config.PageReadConfig;
import com.mavenq.fly.utils.excelv4.model.ExcelFileInfo;
import com.mavenq.fly.utils.excelv4.model.ImportErrorDetail;
import com.mavenq.fly.utils.excelv4.model.ImportResult;
import com.mavenq.fly.utils.excelv4.model.PageReadResult;
import com.mavenq.fly.utils.excelv4.processor.ImportListener;
import com.mavenq.fly.utils.excelv4.processor.PageDataProcessor;
import com.mavenq.fly.utils.excelv4.processor.StreamingDataProcessor;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * 完整的Excel工具类测试 - 文件输出到项目路径
 */
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class ExcelUtilCompleteTest {

    private static final String TEST_OUTPUT_DIR = "test-output";
    private static File testOutputDir;

    private File testExcelFile;
    private File largeExcelFile;
    private File errorExcelFile;

    @BeforeAll // 这个方法只会在整个类开始时执行一次
    static void setUpAll() throws Exception {
        // 创建测试输出目录
        testOutputDir = new File(TEST_OUTPUT_DIR);
        if (!testOutputDir.exists()) {
            testOutputDir.mkdirs();
        }
        System.out.println("测试输出目录: " + testOutputDir.getAbsolutePath());
    }

    @BeforeEach
        // 这个方法会在每个@Test方法执行前都执行一次
    void setUp() throws Exception {
        testExcelFile = new File(testOutputDir, "test_users.xlsx");
        largeExcelFile = new File(testOutputDir, "large_data.xlsx");
        errorExcelFile = new File(testOutputDir, "error_data.xlsx");

        // 生成测试文件
        generateTestFiles();
    }

    private void generateTestFiles() throws Exception {
        // 1. 生成基础测试文件
        List<User> users = TestDataGenerator.generateTestUsers(50);
        String[] headers = {"ID", "姓名", "年龄", "邮箱", "薪资", "创建时间", "状态", "部门"};
        String[] fieldNames = {"id", "name", "age", "email", "salary", "createTime", "status", "department"};

        try (FileOutputStream fos = new FileOutputStream(testExcelFile)) {
            ExcelUtil.exportToExcel(users, headers, fieldNames, "用户数据", fos, true);
            System.out.println("生成测试文件: " + testExcelFile.getAbsolutePath());
        }

        // 2. 生成大文件测试数据
        List<User> largeUsers = TestDataGenerator.generateTestUsers(1000);
        try (FileOutputStream fos = new FileOutputStream(largeExcelFile)) {
            ExcelUtil.exportToExcel(largeUsers, headers, fieldNames, "大数据测试", fos, true);
            System.out.println("生成大文件: " + largeExcelFile.getAbsolutePath());
        }

        // 3. 生成包含错误数据的文件
        List<User> errorUsers = TestDataGenerator.generateUsersWithErrors(20);
        try (FileOutputStream fos = new FileOutputStream(errorExcelFile)) {
            ExcelUtil.exportToExcel(errorUsers, headers, fieldNames, "错误数据测试", fos, true);
            System.out.println("生成错误数据文件: " + errorExcelFile.getAbsolutePath());
        }
    }

    @Test
    @Order(1)
    @DisplayName("基础导出测试")
    void testBasicExport() throws Exception {
        List<User> users = TestDataGenerator.generateTestUsers(10);
        String[] headers = {"ID", "姓名", "年龄", "邮箱"};
        String[] fieldNames = {"id", "name", "age", "email"};

        File outputFile = new File(testOutputDir, "basic_export.xlsx");

        try (FileOutputStream fos = new FileOutputStream(outputFile)) {
            ExcelUtil.exportToExcel(users, headers, fieldNames, "基础导出测试", fos, true);
        }

        assertTrue(outputFile.exists());
        assertTrue(outputFile.length() > 0);
        System.out.println("基础导出文件: " + outputFile.getAbsolutePath());

        // 验证导出的文件可以正常读取
        try (FileInputStream fis = new FileInputStream(outputFile)) {
            List<User> importedUsers = ExcelUtil.importFromExcel(
                    fis, headers, fieldNames, User.class, true);
            assertEquals(users.size(), importedUsers.size());
        }
    }

    @Test
    @Order(2)
    @DisplayName("高级导出测试")
    void testAdvancedExport() throws Exception {
        List<User> users = TestDataGenerator.generateTestUsers(5);
        String[] headers = {"用户ID", "用户姓名", "用户年龄", "电子邮箱", "薪资水平"};
        String[] fieldNames = {"id", "name", "age", "email", "salary"};

        ExcelExportConfig config = new ExcelExportConfig();
        config.setHeaderBgColor(org.apache.poi.ss.usermodel.IndexedColors.DARK_BLUE);
        config.setHeaderFontColor(org.apache.poi.ss.usermodel.IndexedColors.WHITE);
        config.setHeaderFontSize((short) 14);
        config.setHeaderRowHeight((short) 600);
        config.setDataRowHeight((short) 450);
        config.setDefaultColumnWidth(20);
        config.setAutoSizeColumns(true);

        File outputFile = new File(testOutputDir, "advanced_export.xlsx");

        try (FileOutputStream fos = new FileOutputStream(outputFile)) {
            ExcelUtil.exportToExcelAdvanced(users, headers, fieldNames,
                    "高级导出测试", fos, true, config);
        }

        assertTrue(outputFile.exists());
        assertTrue(outputFile.length() > 0);
        System.out.println("高级导出文件: " + outputFile.getAbsolutePath());
    }

    @Test
    @Order(3)
    @DisplayName("基础导入测试")
    void testBasicImport() throws Exception {
        String[] headers = {"ID", "姓名", "年龄", "邮箱", "薪资", "创建时间", "状态", "部门"};
        String[] fieldNames = {"id", "name", "age", "email", "salary", "createTime", "status", "department"};

        try (FileInputStream fis = new FileInputStream(testExcelFile)) {
            List<User> users = ExcelUtil.importFromExcel(fis, headers, fieldNames, User.class, true);

            assertNotNull(users);
            assertFalse(users.isEmpty());
            assertEquals(50, users.size());

            // 验证数据完整性
            User firstUser = users.get(0);
            assertNotNull(firstUser.getId());
            assertNotNull(firstUser.getName());
            assertNotNull(firstUser.getEmail());
            assertTrue(firstUser.getAge() >= 20 && firstUser.getAge() <= 60);

            System.out.println("基础导入测试成功，导入 " + users.size() + " 条数据");
        }
    }

    @Test
    @Order(4)
    @DisplayName("列互换导入测试")
    void testColumnMappingImport() throws Exception {
        ExcelImportConfig<User> config = new ExcelImportConfig<>(User.class)
                .columnMapping("ID", "id")
                .columnMapping("姓名", "name")
                .columnMapping("年龄", "age")
                .columnMapping("邮箱", "email")
                .columnMapping("薪资", "salary")
                .columnMapping("部门", "department");

        try (FileInputStream fis = new FileInputStream(testExcelFile)) {
            List<User> users = ExcelUtil.importExcel(fis, config, true);

            assertNotNull(users);
            assertEquals(50, users.size());

            User user = users.get(0);
            assertNotNull(user.getId());
            assertNotNull(user.getName());
            assertNotNull(user.getEmail());

            System.out.println("列互换导入测试成功");
        }
    }

    @Test
    @Order(5)
    @DisplayName("带校验的导入测试")
    void testImportWithValidation() throws Exception {
        ExcelImportConfig<User> config = new ExcelImportConfig<>(User.class)
                .columnMapping("ID", "id")
                .columnMapping("姓名", "name")
                .columnMapping("年龄", "age")
                .columnMapping("邮箱", "email")
                .requiredFields("name", "email")
                .addFieldValidator("age", (value, fieldName, rowIndex, columnIndex) -> {
                    List<String> errors = new ArrayList<>();
                    if (value != null) {
                        int age = Integer.parseInt(value.toString());
                        if (age < 18 || age > 65) {
                            errors.add("年龄必须在18-65岁之间");
                        }
                    }
                    return errors;
                })
                .addFieldValidator("email", (value, fieldName, rowIndex, columnIndex) -> {
                    List<String> errors = new ArrayList<>();
                    if (value != null && !value.toString().matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
                        errors.add("邮箱格式不正确");
                    }
                    return errors;
                })
                .importListener(new ImportListener<User>() {
                    @Override
                    public void onRowImported(User obj, int rowIndex) {
                        System.out.println("成功导入: " + obj.getName());
                    }

                    @Override
                    public void onRowError(int rowIndex, Exception e) {
                        System.err.println("第" + (rowIndex + 1) + "行导入失败: " + e.getMessage());
                    }
                });

        try (FileInputStream fis = new FileInputStream(errorExcelFile)) {
            ImportResult<User> result = ExcelUtil.importExcelWithValidation(fis, config, true);

            assertNotNull(result);
            // assertTrue(result.getSuccessCount() > 0);
            assertTrue(result.getErrorCount() > 0);

            System.out.println("校验导入结果: " + result.getErrorSummary());

            // 验证错误详情
            for (ImportErrorDetail error : result.getErrors()) {
                System.out.println("错误: " + error.getFullErrorMessage());
            }
        }
    }

    @Test
    @Order(6)
    @DisplayName("固定行读取测试")
    void testFixedRowImport() throws Exception {
        ExcelImportConfig<User> config = new ExcelImportConfig<>(User.class)
                .startRow(5)  // 从第6行开始读取
                .endRow(15)   // 到第16行结束
                .columnMapping("ID", "id")
                .columnMapping("姓名", "name")
                .columnMapping("年龄", "age");

        try (FileInputStream fis = new FileInputStream(testExcelFile)) {
            List<User> users = ExcelUtil.importFromFixedRow(fis, config, true);

            assertNotNull(users);
            assertTrue(users.size() <= 11); // 5-15行，共11行

            System.out.println("固定行读取测试成功，读取 " + users.size() + " 条数据");
        }
    }

    @Test
    @Order(7)
    @DisplayName("分页读取测试")
    void testPageRead() throws Exception {
        ExcelImportConfig<User> config = new ExcelImportConfig<>(User.class)
                .columnMapping("ID", "id")
                .columnMapping("姓名", "name")
                .columnMapping("年龄", "age");

        int page = 1;
        int pageSize = 10;
        int totalPages = 0;
        int totalRecords = 0;

        try (FileInputStream fis = new FileInputStream(largeExcelFile)) {
            do {
                PageReadResult<User> result = ExcelUtil.readPage(fis, config, page, pageSize, true);

                assertNotNull(result);
                assertNotNull(result.getData());
                assertTrue(result.getData().size() <= pageSize);

                totalRecords += result.getData().size();
                System.out.printf("第%d页: %d条数据, 进度: %.1f%%%n",
                        page, result.getData().size(), result.getProgress());

                if (page == 1) {
                    totalPages = result.getTotalPages();
                }

                page++;
            } while (page <= totalPages && page <= 5); // 限制读取前5页

            System.out.println("分页读取完成，共读取 " + totalRecords + " 条数据");
        }
    }

    @Test
    @Order(8)
    @DisplayName("流式读取测试")
    void testStreamingRead() throws Exception {
        ExcelImportConfig<User> config = new ExcelImportConfig<>(User.class)
                .columnMapping("ID", "id")
                .columnMapping("姓名", "name")
                .columnMapping("年龄", "age");

        AtomicInteger processedCount = new AtomicInteger(0);
        AtomicInteger batchCount = new AtomicInteger(0);

        StreamingDataProcessor<User> processor = new StreamingDataProcessor<User>() {
            @Override
            public boolean processItem(User data, int rowIndex) {
                processedCount.incrementAndGet();

                // 模拟业务处理
                if (processedCount.get() % 100 == 0) {
                    System.out.println("已处理: " + processedCount.get() + " 条数据");
                }

                return data.getAge() != null && data.getAge() >= 18;
            }

            @Override
            public void onBatchComplete(int batchSize, int successCount) {
                batchCount.incrementAndGet();
                System.out.printf("第%d批处理完成: 大小=%d, 成功=%d%n",
                        batchCount.get(), batchSize, successCount);
            }

            @Override
            public void onComplete(long totalRows, long successRows) {
                System.out.printf("流式处理完成: 总计=%d, 成功=%d, 成功率=%.2f%%%n",
                        totalRows, successRows, totalRows > 0 ? (successRows * 100.0 / totalRows) : 0);
            }

            @Override
            public void onError(Exception error) {
                System.err.println("流式处理错误: " + error.getMessage());
            }
        };

        try (FileInputStream fis = new FileInputStream(largeExcelFile)) {
            ImportResult<User> result = ExcelUtil.readStreaming(fis, config, processor, true);

            assertNotNull(result);
            assertTrue(processedCount.get() > 0);
            assertTrue(batchCount.get() > 0);

            System.out.println("流式读取测试成功");
        }
    }

    @Test
    @Order(9)
    @DisplayName("异步读取测试")
    void testAsyncRead() throws Exception {
        ExcelImportConfig<User> config = new ExcelImportConfig<>(User.class)
                .columnMapping("ID", "id")
                .columnMapping("姓名", "name")
                .columnMapping("年龄", "age");

        PageReadConfig pageConfig = PageReadConfig.create()
                .pageSize(100)
                .maxRows(500);

        AtomicLong totalProcessed = new AtomicLong(0);
        AtomicInteger pageCount = new AtomicInteger(0);

        PageDataProcessor<User> processor = new PageDataProcessor<User>() {
            @Override
            public boolean processPage(List<User> data, PageReadResult<User> pageInfo) {
                pageCount.incrementAndGet();
                totalProcessed.addAndGet(data.size());

                System.out.printf("异步处理第%d页: %d条数据, 进度: %.1f%%%n",
                        pageInfo.getCurrentPage(), data.size(), pageInfo.getProgress());

                // 模拟处理延迟
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }

                return true;
            }

            @Override
            public void onComplete(long totalRows, long successRows, long errorRows) {
                System.out.printf("异步处理完成: 总计=%d, 成功=%d, 错误=%d%n",
                        totalRows, successRows, errorRows);
            }

            @Override
            public void onError(Exception error) {
                System.err.println("异步处理错误: " + error.getMessage());
            }
        };

        try (FileInputStream fis = new FileInputStream(largeExcelFile)) {
            CompletableFuture<ImportResult<User>> future =
                    ExcelUtil.readAsync(fis, config, processor, pageConfig, true);

            System.out.println("异步任务已启动，主线程继续执行...");

            // 主线程可以执行其他任务
            Thread.sleep(100);

            // 等待异步任务完成
            ImportResult<User> result = future.get(30, TimeUnit.SECONDS);

            assertNotNull(result);
            assertTrue(totalProcessed.get() > 0);
            assertTrue(pageCount.get() > 0);

            System.out.println("异步任务完成: " + result.getErrorSummary());
        }
    }

    @Test
    @Order(10)
    @DisplayName("复杂业务校验测试")
    void testComplexBusinessValidation() throws Exception {
        ExcelImportConfig<User> config = new ExcelImportConfig<>(User.class)
                .columnMapping("ID", "id")
                .columnMapping("姓名", "name")
                .columnMapping("年龄", "age")
                .columnMapping("邮箱", "email")
                .columnMapping("薪资", "salary")
                .columnMapping("部门", "department")
                .requiredFields("name", "email", "department")
                .addFieldValidator("salary", (value, fieldName, rowIndex, columnIndex) -> {
                    List<String> errors = new ArrayList<>();
                    if (value != null) {
                        BigDecimal salary = new BigDecimal(value.toString());
                        if (salary.compareTo(BigDecimal.ZERO) < 0) {
                            errors.add("薪资不能为负数");
                        }
                        if (salary.compareTo(new BigDecimal("1000000")) > 0) {
                            errors.add("薪资不能超过100万");
                        }
                    }
                    return errors;
                })
                .addBusinessValidator((user, rowIndex) -> {
                    List<ImportErrorDetail> errors = new ArrayList<>();

                    // 业务规则1: 技术部员工薪资不能低于5000
                    if ("技术部".equals(user.getDepartment()) &&
                            user.getSalary() != null &&
                            user.getSalary().compareTo(new BigDecimal("5000")) < 0) {
                        errors.add(new ImportErrorDetail(rowIndex, 4, "用户数据",
                                "salary", user.getSalary(), "技术部员工薪资不能低于5000",
                                ImportErrorDetail.ErrorType.BUSINESS_ERROR));
                    }

                    // 业务规则2: 销售部员工年龄不能超过50岁
                    if ("销售部".equals(user.getDepartment()) &&
                            user.getAge() != null && user.getAge() > 50) {
                        errors.add(new ImportErrorDetail(rowIndex, 2, "用户数据",
                                "age", user.getAge(), "销售部员工年龄不能超过50岁",
                                ImportErrorDetail.ErrorType.BUSINESS_ERROR));
                    }

                    return errors;
                });

        try (FileInputStream fis = new FileInputStream(testExcelFile)) {
            ImportResult<User> result = ExcelUtil.importExcelWithValidation(fis, config, true);

            assertNotNull(result);
            System.out.println("复杂业务校验结果: " + result.getErrorSummary());

            // 按错误类型分组显示
            Map<ImportErrorDetail.ErrorType, List<ImportErrorDetail>> errorsByType =
                    result.getErrors().stream()
                            .collect(java.util.stream.Collectors.groupingBy(ImportErrorDetail::getErrorType));

            errorsByType.forEach((type, errors) -> {
                System.out.println("\n" + type + " 错误 (" + errors.size() + " 个):");
                errors.forEach(error ->
                        System.out.println("  " + error.getFullErrorMessage()));
            });
        }
    }

    @Test
    @Order(11)
    @DisplayName("文件信息获取测试")
    void testFileInfo() throws Exception {
        try (FileInputStream fis = new FileInputStream(testExcelFile)) {
            ExcelFileInfo fileInfo = ExcelUtil.getFileInfo(fis, true);

            assertNotNull(fileInfo);
            assertNotNull(fileInfo.getSheetName());
            assertTrue(fileInfo.getRowCount() > 0);
            assertTrue(fileInfo.getColumnCount() > 0);
            assertEquals("xlsx", fileInfo.getFileType());

            System.out.println("文件信息: " + fileInfo.getSheetName() +
                    ", 行数: " + fileInfo.getRowCount() +
                    ", 列数: " + fileInfo.getColumnCount());
        }
    }

    @Test
    @Order(12)
    @DisplayName("性能测试 - 大数据量导出")
    void testPerformanceExport() throws Exception {
        List<User> largeUsers = TestDataGenerator.generateTestUsers(5000);
        String[] headers = {"ID", "姓名", "年龄", "邮箱", "部门"};
        String[] fieldNames = {"id", "name", "age", "email", "department"};

        File outputFile = new File(testOutputDir, "performance_export.xlsx");

        long startTime = System.currentTimeMillis();

        try (FileOutputStream fos = new FileOutputStream(outputFile)) {
            ExcelUtil.exportToExcel(largeUsers, headers, fieldNames, "性能测试", fos, true);
        }

        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;

        assertTrue(outputFile.exists());
        assertTrue(outputFile.length() > 0);

        System.out.printf("导出 %d 条数据耗时: %d ms, 文件: %s%n",
                largeUsers.size(), duration, outputFile.getAbsolutePath());
        assertTrue(duration < 30000, "导出性能测试失败，耗时过长: " + duration + "ms");
    }

    @Test
    @Order(13)
    @DisplayName("多种格式导出测试")
    void testMultipleFormatExport() throws Exception {
        List<User> users = TestDataGenerator.generateTestUsers(10);
        String[] headers = {"ID", "姓名", "年龄", "邮箱"};
        String[] fieldNames = {"id", "name", "age", "email"};

        // 导出为xlsx格式
        File xlsxFile = new File(testOutputDir, "test_xlsx.xlsx");
        try (FileOutputStream fos = new FileOutputStream(xlsxFile)) {
            ExcelUtil.exportToExcel(users, headers, fieldNames, "XLSX格式", fos, true);
        }

        // 导出为xls格式
        File xlsFile = new File(testOutputDir, "test_xls.xls");
        try (FileOutputStream fos = new FileOutputStream(xlsFile)) {
            ExcelUtil.exportToExcel(users, headers, fieldNames, "XLS格式", fos, false);
        }

        assertTrue(xlsxFile.exists());
        assertTrue(xlsFile.exists());

        System.out.println("XLSX文件: " + xlsxFile.getAbsolutePath());
        System.out.println("XLS文件: " + xlsFile.getAbsolutePath());
    }

    @Test
    @Order(14)
    @DisplayName("自定义样式导出测试")
    void testCustomStyleExport() throws Exception {
        List<User> users = TestDataGenerator.generateTestUsers(8);
        String[] headers = {"员工ID", "员工姓名", "员工年龄", "工作邮箱", "所在部门", "薪资等级"};
        String[] fieldNames = {"id", "name", "age", "email", "department", "salary"};

        ExcelExportConfig config = new ExcelExportConfig();
        config.setHeaderBgColor(org.apache.poi.ss.usermodel.IndexedColors.GREEN);
        config.setHeaderFontColor(org.apache.poi.ss.usermodel.IndexedColors.WHITE);
        config.setHeaderFontSize((short) 12);
        config.setHeaderBold(true);
        config.setDataAlignment(org.apache.poi.ss.usermodel.HorizontalAlignment.CENTER);
        config.setDefaultColumnWidth(15);
        config.setAutoSizeColumns(true);

        File outputFile = new File(testOutputDir, "custom_style_export.xlsx");

        try (FileOutputStream fos = new FileOutputStream(outputFile)) {
            ExcelUtil.exportToExcelAdvanced(users, headers, fieldNames,
                    "自定义样式", fos, true, config);
        }

        assertTrue(outputFile.exists());
        System.out.println("自定义样式导出文件: " + outputFile.getAbsolutePath());
    }

    @Test
    @Order(15)
    @DisplayName("错误处理测试")
    void testErrorHandling() throws Exception {
        // 测试不存在的文件
        File notExistsFile = new File(testOutputDir, "not_exists.xlsx");

        assertThrows(Exception.class, () -> {
            try (FileInputStream fis = new FileInputStream(notExistsFile)) {
                ExcelUtil.importFromExcel(fis, new String[]{"ID"}, new String[]{"id"}, User.class, true);
            }
        });

        System.out.println("错误处理测试完成 - 异常被正确抛出");
    }

    @AfterAll
    static void tearDownAll() {
        // 测试完成后可以手动清理文件，或者保留用于检查
        System.out.println("测试完成！所有测试文件保存在: " + testOutputDir.getAbsolutePath());
        System.out.println("可以手动检查生成的Excel文件");

        // 列出生成的所有文件
        if (testOutputDir.exists() && testOutputDir.isDirectory()) {
            File[] files = testOutputDir.listFiles();
            if (files != null) {
                System.out.println("生成的测试文件列表:");
                for (File file : files) {
                    System.out.println("  - " + file.getName() + " (" + file.length() + " bytes)");
                }
            }
        }
    }
}